package genesis.familytree.server.common.code;


import genesis.familytree.server.common.code.builder.*;
import genesis.familytree.server.common.exception.AttributeValueNotFoundException;
import genesis.familytree.server.common.exception.ExpressionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 表达式处理器
 */
public class Expression {
    private static final Logger LOG = LoggerFactory.getLogger(Expression.class);

    private static final List<DataNameBuilder> DATA_NAME = new ArrayList<>();
    private static final Map<String, DataTypeBuilder> DATA_TYPE = new HashMap<>();

    static {
        addBuilder("date", new TimeExprBuilder());
        addBuilder("number", new NumberExprBuilder());
        addBuilder("mobile", new MobileExprBuilder());
        addBuilder("hidden", new HiddenExprBuilder());
        addBuilder("script", new ScriptExprBuilder());
        addBuilder("env", new EnvExprBuilder());
        addBuilder("string", new StringExprBuilder());
    }

    public static void addBuilder(String type, Object obj) {
        if (obj instanceof DataNameBuilder) {
            DATA_NAME.add((DataNameBuilder) obj);
        }

        if (obj instanceof DataTypeBuilder) {
            DATA_TYPE.put(type, (DataTypeBuilder) obj);
        }
    }

    private String expression;
    private List<Attribute> attrs = new ArrayList<Attribute>();


    /**
     * 构架方法
     * @param expression 要解析的表达式
     */
    public Expression(String expression) {
        this.expression = expression;

        CodeMatcher matcher = new CodeMatcher(expression);

        if (LOG.isDebugEnabled()) {
            LOG.debug("parse expression: " + expression);
        }

        while (matcher.find()) {
//            String key = matcher.group();
            String code = matcher.group();
            if (LOG.isDebugEnabled()) {
                LOG.debug("find attribute : #{" + code + "}");
            }

            Attribute attr = null;
            for (DataNameBuilder dataName : DATA_NAME) {
                int startIndex = matcher.start();
                int endIndex = matcher.end();

                attr = dataName.build(this, startIndex, endIndex, code);
                if (null != attr) {
                    break;
                }
            }
            if (null == attr) {
                throw new IllegalArgumentException("表达式错误");
            }

            attrs.add(attr);
        }
    }

    /**
     * 获取解析结果
     * @param params 可以替换占位符的值
     * @return 返回解析结果
     * @throws ScriptException
     */
    public String getValue(Params params) throws ScriptException {
        Map<String, String> ps = new HashMap<>();

        StringBuilder sb = new StringBuilder();
        int start = 0;

        for (Attribute attr : attrs) {
            sb.append(expression.substring(start, attr.getStartIndex()));

            String code = attr.getExpr();

            if (LOG.isDebugEnabled()) {
                LOG.debug("get attribute[ " + code + " ] value");
            }

            if(ps.containsKey(code)) {
                String result = ps.get(code);

                if (LOG.isDebugEnabled()) {
                    LOG.debug("attribute value [ cache value=" + result + " ] ");
                }

                sb.append( result);
            }else {
                String name = attr.getCode();
                String type = attr.getType();
                String format = attr.getFormat();

                if (LOG.isDebugEnabled()) {
                    LOG.debug("attribute [ name=" + name + ", type=" + type + ", format=" + format + " ] ");
                }

                DataTypeBuilder dataTypeBuilder = DATA_TYPE.get(type);
                if (null == dataTypeBuilder) {
                    throw new IllegalArgumentException("expression[" + expression + "] param[{" + attr + "}] unknown type");
                }

                try {
                    String result = dataTypeBuilder.build(name, format, params);
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("attribute value [ value=" + result + " ] ");
                    }
                    ps.put(attr.getExpr(), result);
                    sb.append( result);
                } catch (ExpressionException ex) {
                    if (ex instanceof AttributeValueNotFoundException) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("attribute[ " + attr.getExpr() + " ] value not found ");
                        }
                    }
                    sb.append(expression.substring(attr.getStartIndex(), attr.getEndIndex()));
                }

            }

            start = attr.getEndIndex();
        }

        sb.append(expression.substring(start));

        return sb.toString();
    }

    public String getExpression() {
        return expression;
    }

    @Override
    public String toString() {
        return this.expression;
    }
}
